﻿/******************************************************************************************************
【技术文档】
Kister转换规则 http://121.199.10.158:8107/c5p8d4he6gjkxm0oq1zrbslt2v37wify9aun/haos_20211006/qdas/qingshan.html#08-kistler

【配置文件】
程序目录下有配置文件 C2021T08_Kisler_pathlist.txt 用于记录需要转换的机器。若此文件不存在，程序则会自动创建，并添加几个测试路径。

【变更记录】
2022/04/04  hao  1. K0010，K0004，K0014 从文件名中提取 -> 从文件中提取
                 2. 网络结构发生变化，现在从同一台机器提取，所以不需要再考虑网络路径的情况。

*******************************************************************************************************/

using QDAS;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace QDasConverter.Core
{
    public class C2021T08_Kisler : ConvertBase
    {
        /// <summary>
        /// 需要进行转换的数据目录，每个目录下包括Kistler的CSV文件列表。注意：程序不转换子目录下的CSV文件。
        /// </summary>
        List<string> dataPaths = new List<string>();

        // 路径配置文件
        string data_paths_file = "configs\\C2021T08_Kisler_pathlist.txt";

        // 转换时所需要用到的K域。
        public Dictionary<string, string[]> kfields = new Dictionary<string, string[]>();

        public C2021T08_Kisler()
        {
            NeedConnectionCheck = false;
            DisplayName = "Kister 转换器";
            Version = "beta1 Released Date: 2021/12/01 Inner Name: C2021T08_Kisler";
            Version = "beta2 Released Date: 2022/04/04 Inner Name: C2021T08_Kisler";
        }

        /// <summary>
        /// 设置为True，保证一直转换。
        /// </summary>
        /// <returns></returns>
        public override bool NeedConvert() => true;


        public override bool OnStart()
        {
            LoadKFields();

            // 测试模式使用
            if (DebugMode)
            {
                ProcessDirectory(@"..\..\samples\kistler\MP-009\");
            } 

            // ReadDataPaths返回所有kistler一级目录，其二级目录表示对应压机
            // 本循环用于获得指定路径的所有子目录
            foreach (var path in ReadDataPaths(data_paths_file))
            {
                // 判断目录是否存在
                if (!Directory.Exists(path))
                    continue;

                var subdirs = Directory.GetDirectories(path);
                foreach (var subdir in subdirs)
                {
                    var dir = subdir.TrimEnd('\\').TrimEnd('/');
                    if (!dir.EndsWith("_backup") && !dir.EndsWith("_output"))
                        dataPaths.Add(dir);
                }
            }

            // lastConvertTime = ia.ReadDateTime("lastConvertTime", DateTime.Now);

            return true;
        }

        public override void OneLoop_Prepare()
        {


        }

        public override void OneLoop_Process()
        {
            try
            {
                // 对所有的目录进行遍历操作
                for (int i = 0; i < dataPaths.Count; i++)
                {
                    ProcessDirectory(dataPaths[i]);
                    Thread.Sleep(100);
                }
            }catch (Exception ex)
            {
                Common.AddLog(ex.Message);
            }
        }

        public void LoadKFields()
        {
            var path = Path.GetFullPath(Path.Combine(@"configs\C2021T08_kistler_kfields.csv"));
            // 0.设备	 1.工位号	    2.工序名称   3.机器       4.机器编号    5.拧紧力矩下限	6.拧紧力矩上限   7.力矩单位   8.角度下限  9.角度上限 10.角度单位
            // 齐世乐	     DCT-0200	轴承外圈压装	DCT0200-R	110090	      10	         15             KN            201      204       mm 
            var rows = File.ReadAllLines(path, Encoding.Default);
            for (int i = 1; i < rows.Length; i++)
            {
                var cells = rows[i].Split(',');
                if (cells.Length == 11 && !kfields.ContainsKey(cells[3]))
                    kfields.Add(cells[3], cells);
            }
        }

        private void ProcessDirectory(string dataPath)
        {
            var path = dataPath.Split('|')[0];
            var pathb = path + "_backup"; // 备份文件夹
            if (!Directory.Exists(pathb))
                Directory.CreateDirectory(pathb);

            DateTime onedayago = DateTime.Today.AddDays(-1);
            foreach (var csvfilepath in Directory.GetFiles(path))
            {
                string K1001 = "";
                string K0010 = "";
                //设备名
                string devicename = "";
                string K0014 = "";
                DateTime K0004 = DateTime.Now;

                /**********************************************************************
                 * 原方法：从文件名中提取基础信息，2022/04/04被废除
                 * *******************************************************************/
                //{
                //    // 文件名： Part_DCT0200-L_MP-012_2021-11-12_06-52-24_A20131TJMLC0113H0_OK.csv
                //    // var csvfilepath1 = @"D:\shared\kistler\MP-009\Part_DCT0200-L_MP-002_2021-12-19_00-32-29_S40135C150001000B1500MM00113_OK.csv";
                //    var kf = KistlerFileInfo.Parse(csvfilepath);
                //    if (kf != null)// || kf.TestTime < onedayago)
                //    {
                //        // 提取零件名
                //        K1001 = kf.PartName;
                //        // 2021/12/30 将当前时间改为文件名中的时间作为测量时间
                //        K0004 = kf.TestTime;
                //        // 2021/12/08 暂时不区别压机号，直接从第1位转换
                //        if (kfields.ContainsKey(kf.PartName))
                //            K0010 = kfields[kf.PartName][4];
                //    }
                //}

                /**********************************************************************
                 * 新方法：直接从文件中提取基础信息，2022/04/04 添加，其中：
                 *   K1001 从开头为 XXX 的行提取
                 *   K0010 中的日期从开头为 Date 的行提取，如 Date;2022/03/29
                 *         中的时间从开头为 Time 的行提取，如 Time;15:42:45
                 *   K0014 从开头为 Part serial number 的行提取，如 Part serial number;TSA11005167ANC290156
                 * *******************************************************************/
                var lines = File.ReadAllLines(csvfilepath, Encoding.UTF8);

                List<string> list = new List<string>();
                string adate = "", atime = "";
                for (int j = 0; j < lines.Length; j++)
                {
                    var row = lines[j];
                    // K1001 对应 Measuring program name
                    if (K1001.Length == 0 && row.StartsWith("Measuring program name;"))
                        K1001 = row.Split(';')[1].Trim();
                    // K0014 对应 
                    else if (K0014.Length == 0 && row.StartsWith("Part serial number;"))
                        K0014 = row.Split(';')[1].Trim();
                    // K0004.Date 对应 Date
                    else if (adate.Length == 0 && row.StartsWith("Date;"))
                        adate = row.Split(';')[1].Trim();
                    // K0004.Time 对应 Time
                    else if (atime.Length == 0 && row.StartsWith("Time;"))
                        atime = row.Split(';')[1].Trim();
                    // K0010 对应 Network name
                    else if (K0010.Length == 0 && row.StartsWith("Network name;"))
                        if (kfields.ContainsKey(row.Split(';')[1].Trim())) {
                            K0010 = kfields[row.Split(';')[1].Trim()][4];
                            devicename = row.Split(';')[1].Trim();
                        }
                           

                    // 如果遇到Measuring curve，表示下面都是曲线，则提取完后就结束循环
                    if (!lines[j].StartsWith("Measuring curve"))
                        continue;
                    // 以下是具体曲线数据提取代码。
                    j += 10;
                    while (lines[j].Length > 0 && char.IsDigit(lines[j][0]))
                    {
                        list.Add(lines[j]);
                        j++;
                    }
                }
                K0004 = Common.ParseDatetime($"{adate} {atime}");


                /**********************************************************************
                 * DFQ 数据转换模块
                 * *******************************************************************/
                QFile qf = new QFile();
                qf[1001] = K1001;
                qf[1002] = K1001;

                // 拧紧力矩
                var qcTorque = qf.AddQCharacteristic();
                qcTorque[2001] = "1";
                qcTorque[2002] = "压力";
                if (kfields.ContainsKey(devicename))
                {
                    qcTorque[2110] = kfields[devicename][5];
                    qcTorque[2111] = kfields[devicename][6];
                }
                qcTorque[2142] = "kN";

                // 拧紧角度
                var qcAngle = qf.AddQCharacteristic();
                qcAngle[2001] = "2";
                qcAngle[2002] = "位移";
                if (kfields.ContainsKey(devicename))
                {
                    qcAngle[2110] = kfields[devicename][8];
                    qcAngle[2111] = kfields[devicename][9];
                }
                qcAngle[2142] = "mm";



                /**********************************************************************
                 * 曲线提取模块，2022/01/15 加入，满足以下两点修改内容：
                 *  Time; X(ABSOLUTE); Y; X (ABSOLUTE)
                 *  0,2184; 215,77600; -0,00384; 215,77600
                 *  s; mm; kN; mm
                 *  0,0000; 214,97540; -0,00265; 214,97540
                 *  0,2182; 215,77600; -0,03494; 215,77600
                 * *******************************************************************/
                StringBuilder sbTorque = new StringBuilder(); // 扭距
                StringBuilder sbAngle = new StringBuilder();  // 扭力
                //sbTorque.Append("time,kN;");
                //sbAngle.Append("time,mm;");
                var idTorque = $"08_{K0004:yyyyMMddHHmmss}_01_{list.Count:0000}_{random.NextDouble()*1000000:0}";
                var idAngle = $"08_{K0004:yyyyMMddHHmmss}_02_{list.Count:0000}_{random.NextDouble()*1000000:0}";
                for (int i = 0; i < list.Count; i++)
                {
                    // time,mm,kN;0.0000,214.97590,0.00847;0.2184,215.77600,0.02686;0.3483,216.41750,-0.04465;
                    var row = list[i];
                    var items = row.Replace(',', '.').Split(';');

                    sbTorque.Append($"{items[0]},{items[2]};");
                    sbAngle.Append($"{items[0]},{items[1]};");

                    // 以下代码用于取最后一组值，作为终值。
                    if (i == list.Count - 1 && (qcAngle.data.Count == 0 && qcTorque.data.Count == 0))
                    {
                        var value_time = K0004.AddSeconds(double.Parse(items[0]));
                        var di1 = qcTorque.AddItem();
                        di1[0010] = K0010;
                        di1[0014] = K0014;
                        di1[0017] = idTorque;
                        di1.value = double.Parse(items[2]);
                        di1.date = value_time;

                        var di2 = qcAngle.AddItem();
                        di2[0010] = K0010;
                        di2[0014] = K0014;
                        di2[0017] = idAngle;
                        di2.value = double.Parse(items[1]);
                        di2.date = value_time;
                    }
                }

                // 输出第1条曲线数据：Torque
                File.WriteAllText(Path.Combine(OutputDirectory, idTorque + ".txt"), sbTorque.ToString());

                // 输出第2条曲线数据: Angle
                File.WriteAllText(Path.Combine(OutputDirectory, idAngle + ".txt"), sbAngle.ToString());

                SaveToDFQ(qf, OutputDirectory, $"C08_{K1001}_{K0004:yyyyMMddHHmmss}_{NowString()}.dfq");
                CopyFileTo(csvfilepath, pathb, true);
            }
        }

        public override void OneLoop_Completed()
        {
            // ia.WriteValue("lastConvertTime", lastConvertTime.ToString("yyyy-MM-dd HH:mm:ss"));
        }

        /// <summary>
        /// 将输入文件移动到指定的目录，文件名不变。
        /// </summary>
        /// <param name="srcfilepath">输入文件。</param>
        /// <param name="targetdir">目标文件夹。</param>
        /// <param name="deleteScr">是否删除原文件，默认值为false。</param>
        /// <param name="deleteTarget">是否删除目标文件，只有目标文件存在时才生效，默认值为true。</param>
        public void CopyFileTo(string srcfilepath, string targetdir, bool deleteScr = false, bool deleteTarget = true)
        {
            // 输出目标的文件名
            var targetFilepath = Path.Combine(targetdir, Path.GetFileName(srcfilepath));

            // 如果目标文件存在且需要删除，则删除目标文件
            if (File.Exists(targetFilepath) && deleteTarget)
                File.Delete(targetFilepath);

            // 在目标文件不存在的情况下才复制
            if (!File.Exists(targetFilepath))
                File.Copy(srcfilepath, targetFilepath);

            // deleteScr决定是否需要删除源文件
            if (deleteScr)
                File.Delete(srcfilepath);
        }

    }


    /// <summary>
    /// 用于表示CSV文件名，以从中解析出部件名，型号，测试时间等信息。
    /// CSV数据文件格式示例：Part_DCT0200-L_MP-012_2021-11-12_06-52-24_A20131TJMLC0113H0_OK.csv
    /// </summary>
    public class KistlerFileInfo
    {
        public string PartName;
        public string Model;
        public DateTime TestTime;
        public string Description;
        public string TestResult;


        public static KistlerFileInfo Parse(string path)
        {
            if (string.IsNullOrEmpty(path))
                return null;

            path = Path.GetFileNameWithoutExtension(path);

            // Part_DIFF0030_MP-009_2019-03-01_03-11-15_KB00002_OK.csv
            var strs = path.Split('_');
            if (strs.Length != 7)
                return null;

            KistlerFileInfo kfi = new KistlerFileInfo();
            try
            {
                // 0:Part, 1:DIFF0030, 2:MP-009, 3:2019-03-01, 4:03-11-15, 55:KB00002, 6:OK.csv
                kfi.PartName = strs[1];
                kfi.Model = strs[2];
                kfi.TestTime = DateTime.Parse(strs[3] + " " + strs[4].Replace('-', ':'));
                kfi.Description = strs[5];
                kfi.TestResult = strs[6].Replace(".csv", "");
            }
            catch { kfi = null; }

            return kfi;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"PartName: {PartName}, ");
            sb.Append($"Model: {Model}, ");
            sb.Append($"TestTime: {TestTime:yyyy/MM/dd HH:mm:ss}, ");
            sb.Append($"Description: {Description}, ");
            sb.Append($"TestResult: {TestResult}.");
            return sb.ToString();
        }


    }





}
